package com.xxx.protobuf.server;

import com.xxx.protobuf.MessageProto;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

import java.net.InetSocketAddress;

/**
 * protobuf： server端
 */
public class ProtobufServer {
    private final int port;

    public ProtobufServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .localAddress(new InetSocketAddress(port))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) {
                     ChannelPipeline pipeline = ch.pipeline();
                     
                     // 添加 Protobuf 相关编解码器
                     /**
                      * 说明：
                      * ProtobufVarint32FrameDecoder:
                      *     处理 protobuf 消息头（基于长度的变长编码）
                      *     解决 TCP 粘包/拆包问题
                      * ProtobufDecoder:
                      *     将字节流解码为 Protobuf 消息对象
                      *     需要指定默认实例：MessageProto.Request.getDefaultInstance()
                      * ProtobufVarint32LengthFieldPrepender:
                      *     在消息前添加长度字段
                      * ProtobufEncoder:
                      *     将 Protobuf 消息编码为字节流
                      */
                     pipeline.addLast(new ProtobufVarint32FrameDecoder());
                     pipeline.addLast(new ProtobufDecoder(MessageProto.Request.getDefaultInstance()));
                     pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
                     pipeline.addLast(new ProtobufEncoder());
                     
                     // 添加自定义业务处理器
                     pipeline.addLast(new ProtobufServerHandler());
                 }
             });

            ChannelFuture f = b.bind().sync();
            System.out.println("Server started on port " + port);
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new ProtobufServer(8080).start();
    }
}